/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: CollectorSkeleton.java 519 2007-05-07 14:59:54Z jacek.kolezynski $
 */
package logmatcher.collectors;

import logmatcher.Collector;
import logmatcher.Configurable;
import logmatcher.InitializationException;
import logmatcher.InputFilter;
import logmatcher.LogEventListener;
import logmatcher.LogEventProducer;
import logmatcher.Utils;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.EOFException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Collections;


/**
 * Abstract superclass for collectors. Provides the code for common functionality.
 *
 * @version $Revision: 519 $
*/
public abstract class CollectorSkeleton implements Collector, Configurable, LogEventProducer, Runnable {
    //~ Static fields/initializers -----------------------------------------------------------------------------------

    /** Class Logger */
    private static final Log LOGGER = LogFactory.getLog(CollectorSkeleton.class);

    //~ Instance fields ----------------------------------------------------------------------------------------------

    /** Thread executing the logic. */
    private Thread worker = new Thread(this);

    /** <code>true</code> if the collector is going to be stopped. */
    protected boolean stopped = false;

    protected List<LogEventListener> listeners = Collections.synchronizedList(new ArrayList<LogEventListener>());

    //~ Constructors -------------------------------------------------------------------------------------------------

/**
     * Creates a new CollectorSkeleton object.
     */
    public CollectorSkeleton() {
        super();
    }

    //~ Methods ------------------------------------------------------------------------------------------------------

    /**
     * Stop collector.
     *
     * @throws IOException in case of problem while closing stream.
     */
    public void stop() throws IOException {
        stopped = true;
        worker = null;
    }


    /**
     * Start working thread.
     *
     * @throws IOException if error occurs while opening the connection with source of events.
     */
    public void start() throws IOException {
    	if(worker == null)
    		worker = new Thread(this);
        worker.start();
    }


    /**
     * This implementation do not support multiple listeners. Next call of this method overwrites previous
     * assignment.
     *
     * @see logmatcher.LogEventProducer#addLogEventListener(logmatcher.LogEventListener)
     */
    public void addLogEventListener(LogEventListener listener) {
    	if(listener == null){
    		throw new IllegalArgumentException("Argument cannot be null");
    	}
        listeners.add(listener);
    }


    /**
     * 
     * @see logmatcher.LogEventProducer#addLogEventListener(logmatcher.LogEventListener, logmatcher.InputFilter)
     */
    public void addLogEventListener(LogEventListener listener, InputFilter filter) {
        throw new UnsupportedOperationException("This implementation do not support conditional subscriptions");
    }


    /**
     * Read message from the current source.
     *
     * @return collected message.
     *
     * @throws IOException if reading error occurs.
     * @throws UnhandledException in case of any other error.
     */
    protected abstract Object readMessage() throws IOException, UnhandledException;


    /**
     * Read messages from the stream.
     */
    public void run() {
        try {
            while (!stopped) {
                Object message = readMessage();

                if (message == null) {
                    stop();
                }else {
                	notifyAllListeners(message);
                }
            }
        } catch (EOFException e) {
            LOGGER.info("End of stream reached, closing the connector");
        } catch (IOException e) {
            LOGGER.error("Error while reading input", e);
        } catch (UnhandledException e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            try {
                stop();
            } catch (IOException e) {
                LOGGER.error("Error while closing collector", e);
            }
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Configured object: " + ToStringBuilder.reflectionToString(this));
        }
    }
    
    /**
     * Helper method for notifying all log event listeners.
     * @param message the message
     */
    private void notifyAllListeners(Object message){
    	for(LogEventListener l : listeners){
    		l.receiveLogEvent(message);
    	}
    }


    /**
     * Initialize this collector. 'logmatcher.logbuffer' property in configuration should contain
     * <code>LogBuffer</code> instance.
     *
     * @param config Configuration object.
     * @param id prefix for config elements of this object.
     *
     * @throws InitializationException in case <code>Configuration</code> instance do not contain implementation of
     *         <code>LogBuffer</code>.
     *
     * @see logmatcher.Configurable#configure(org.apache.commons.configuration.Configuration, java.lang.String)
     */
    public final void configure(Configuration config, String id)
        throws InitializationException {
        Utils.autoConfigure(this, config, id);
        configureCollector(config, id);
    }


    /**
     * Perform additional actions to customize this collector. Called from configure(..) and is to be
     * overridden by subclasses of <code>CollectorSkeleton</code>.
     *
     * @param config Configuration instance.
     * @param id prefix of collector's properties.
     */
    protected void configureCollector(Configuration config, String id) {
        // there is no need to configure anything here
    }
}
